//
//
//
//
//
//
// NetCDR.C
//

#include "NetCDR.h"
#include "common.h"
#include "Exception.h"

NetCDR::NetCDR() : _size(0), _capacity(NetCDR::DEFAULT_LOCAL_BUFSIZE),
	_rdPtr(_szLocal), _wtPtr(_szLocal), _bLocal(true), _ext(NULL)
{
	memset(_szLocal, 0, sizeof(_szLocal));
}

NetCDR::NetCDR(U32 size) : _size(0)
{
	memset(_szLocal, 0, sizeof(_szLocal));
	if(size <= NetCDR::DEFAULT_LOCAL_BUFSIZE)
	{
		_capacity = NetCDR::DEFAULT_LOCAL_BUFSIZE;
		_rdPtr = _szLocal;
		_wtPtr = _szLocal;
		_bLocal = true;
		_ext = NULL;
	}
	else
	{
		U32 h = size >> 3;
		U32 l = size % 8;

		_capacity = l ? (h + 1) * 8 : h* 8;
		_ext = new char[_capacity];

		// rd ptr and wt ptr should be the start of data buf
		_rdPtr = _ext;
		_wtPtr = _ext;
		_bLocal = false;
	}
}

NetCDR::NetCDR(const NetCDR& cdr) : _size(cdr._size),
	_capacity(cdr._capacity),
	_bLocal(cdr._bLocal),
	_ext(NULL)
{
	if(_bLocal)
	{
		memcpy(_szLocal, cdr._szLocal, DEFAULT_LOCAL_BUFSIZE);
		_rdPtr = _wtPtr = _szLocal;
	}
	else
	{
		_ext = new char[_capacity];
		memcpy(_ext, cdr._ext, _capacity);
		_rdPtr = _wtPtr = _ext;
	}
}

NetCDR::NetCDR(const char* data, U32 size)
{
	if(size <= DEFAULT_LOCAL_BUFSIZE)
	{
		memcpy(_szLocal, data, size);
		_rdPtr = _wtPtr = _szLocal;
		_ext = NULL;
		_capacity = DEFAULT_LOCAL_BUFSIZE;
		_size = size;
	}
	else
	{
		U32 h = size >> 3;
		U32 l = size % 8;
		U32 n = l ? (h + 1) * 8 : h * 8;

		_ext = new char[n];
		memcpy(_ext, data, size);
		_rdPtr = _wtPtr = _ext;
		_capacity = n;
		_size = size;
	}
}


NetCDR::~NetCDR()
{
	delete _ext;	
}



//
//
//set read ptr pos, throw exception if exceed
// 
char* NetCDR::RdPtr()
{
	 return _rdPtr;
}

//
//
// set write ptr pos, throw exception if exceed
//
char* NetCDR::WtPtr()
{
	return _wtPtr;
}

//
//
//
void NetCDR::ChkExceed(void* _newPtr)
{
	if(_newPtr)
	{
		if(_bLocal)
		{
			if(_newPtr > _szLocal + DEFAULT_LOCAL_BUFSIZE ||
					_newPtr < _szLocal)
			{
				throw Exception("Set NetCDR::RdPtr exceed");
			}
		}
		if(!_bLocal)
		{
			if(_newPtr > (char*)_ext + DEFAULT_LOCAL_BUFSIZE ||
				_newPtr < _ext)
			{
				throw Exception("Set NetCDR::RdPtr heap exceed");
			}
		}
	}
}

//
//
//
// check whether big endian
//

bool NetCDR::BigEndian()
{
	int a = 0x31;
	char b = (*(char*)&a);
	return b == '1' ? false : true;
}

void NetCDR::OrderChg(void* dst, void* src, U32 size)
{
	if(NULL == dst || NULL == src)
		throw Exception("NULL ptr when NetCDR::OrderChg");

	char* pDst = (char*)dst;
	char* pSrc = (char*)src;
	for(U32 i = 0; i < size; i++)
		pDst[i] = pSrc[size - i -1];
}

//
//
//
NetCDR& NetCDR::operator<<(U8 info)
{
	NeedEnlg(1);
	U8 _info = info;

	if(!BigEndian())
		OrderChg(&_info, &info, 1);
	memcpy(_wtPtr, &_info, 1);
	_wtPtr += 1;
	_size += 1;
	return *this;
}

NetCDR& NetCDR::operator<<(S8 info)
{
	U8 tmp = (U8)info;
	return (*this << tmp);
}


//
//
//
NetCDR& NetCDR::operator<<(U16 info)
{
	NeedEnlg(2);
	U16 _info = info;

	if(!BigEndian())
		OrderChg(&_info, &info, 2);
	memcpy(_wtPtr, &_info, 1);
	_wtPtr += 2;
	_size += 2;
	return *this;
}

NetCDR& NetCDR::operator<<(S16 info)
{
	U16 tmp = (U16)info;
	return (*this << tmp);
}

//
//
//
NetCDR& NetCDR::operator<<(U32 info)
{
	NeedEnlg(4);
	U32 _info = info;

	if(!BigEndian())
		OrderChg(&_info, &info, 4);
	memcpy(_wtPtr, &_info, 4);
	_wtPtr += 4;
	_size += 4;
	return *this;
}

NetCDR& NetCDR::operator<<(S32 info)
{
	U32 tmp = (U32)info;
	return (*this << tmp);
}

//
//
//
NetCDR& NetCDR::operator<<(const std::string& info)
{
	NeedEnlg(info.length() + 1);
	memcpy(_wtPtr, info.c_str(), info.length() + 1);
	_wtPtr += info.length() + 1;
	_size += info.length() + 1;
	return *this;
}

NetCDR& NetCDR::operator>> (U8& info)
{
	ChkExceed(_rdPtr + 1);
	memcpy(&info, _rdPtr, 1);

	_rdPtr += 1;
	return *this;
}


NetCDR& NetCDR::operator>> (S8& info)
{
	U8 tmp;
	*this >> tmp;
	info = (S8)tmp;
	return *this;
}

NetCDR& NetCDR::operator>> (U16& info)
{
	ChkExceed(_rdPtr + 2);
	U16 _info = 0;
	U16 _extInfo = 0;

	memcpy(&_info, _rdPtr, 2);
	if(!BigEndian())
	{
		OrderChg(&_extInfo, &_info, 2);
		info = _extInfo;
	}
	else
		info = _info;
	_rdPtr += 2;
	return *this;
}

NetCDR& NetCDR::operator>> (S16& info)
{
	U16 tmp;
	*this >> tmp;
	info = (S16)tmp;
	return *this;
}

NetCDR& NetCDR::operator>> (U32& info)
{
	ChkExceed(_rdPtr + 4);
	U32 _info = 0;
	U32 _extInfo = 0;

	memcpy(&_info, _rdPtr, 4);
	if(!BigEndian())
	{
		OrderChg(&_extInfo, &_info, 4);
		info = _extInfo;
	}
	else
		info = _info;
	_rdPtr += 4;
	return *this;
}

NetCDR& NetCDR::operator>> (S32& info)
{
	U32 tmp;
	*this >> tmp;
	info = (S32)tmp;
	return *this;
}

NetCDR& NetCDR::operator>> (std::string& info)
{
	U32 left = strlen(_rdPtr);
	if(0 == left)
		return *this;

	info = (char*)_rdPtr;
	_rdPtr += left + 1;
	return *this;
}

NetCDR& NetCDR::operator>> (char* buf)
{
	U32 left = strlen(_rdPtr);
	if(0 == left)
		return *this;

	memcpy(buf, _rdPtr, left + 1);
	_rdPtr += left + 1;
	return *this;
}

void NetCDR::NeedEnlg(U32 num)
{
	if(_capacity - _size >= num)
		return;
	
	U32 h = num >> 3;
	U32 l = num % 8;

	U32 rn = _rdPtr - (_bLocal ? _szLocal : _ext);
	U32 wn = _wtPtr - (_bLocal ? _szLocal : _ext);

	U32 n = l ? h * 8 : (h + 1) * 8;
	_capacity += n;

	char* p = new char[_capacity];
	char* pSrc = _bLocal ? _szLocal : _ext;
	memcpy(p, pSrc, _size);
	delete _ext;
	_ext = p;
	_bLocal = false;

	_rdPtr = (char*)_ext + rn;
	_wtPtr = (char*)_ext + wn;
}

char* NetCDR::Data()
{
	return _bLocal ? _szLocal : _ext;
}

bool NetCDR::EndBit(void)
{
	if(_bLocal)
		return _rdPtr == _szLocal + _size;
	return _rdPtr == _ext + _size;
}

void NetCDR::Resize(U32 _newSize)
{
	if(_newSize <= _capacity)
		return;
	NeedEnlg(_newSize - _size);
}

//
//
// clear the content
void NetCDR::Clr(void)
{
	if(_ext)
	{
		memset(_ext, 0, _capacity);
		_rdPtr = _wtPtr = _ext;
		_size = 0;
		return;
	}

	memset(_szLocal, 0, DEFAULT_LOCAL_BUFSIZE);
	_rdPtr = _wtPtr = _szLocal;
}


U32 NetCDR::Size()
{
	return _size;
}

U32 NetCDR::FreeBytes()
{
	return _capacity - _size;
}
